package rpcdriver

import (
	"encoding/json"
	"fmt"
	vo2 "gluenet/internal/glue-srvp/domain/vo"
	infra2 "gluenet/internal/pkg/infra"

	"time"

	"github.com/wutianze/nats.go"
)

const (
	RPC_TRY_TIME     = 1
	RPC_TRY_INTERVAL = 30
	RPC_TIME_OUT     = 60
)

var isOnline = false

type NatRpcDriver struct {
	nc *nats.Conn
}

func NewDriver(url string) (*NatRpcDriver, error) {
	nc, err := nats.IConnect(url)

	if err != nil {
		return nil, err
	}

	return &NatRpcDriver{
		nc: nc,
	}, nil
}

func (r *NatRpcDriver) Register(req *vo2.AgentRegisterReq, resp *vo2.AgentRegisterRes) error {
	uri := `v2.rpc.apis.agent.regsiter`

	bt, err := json.Marshal(req)
	if err != nil {
		return err
	}

	rlt, err := r.nc.Request(uri, bt, RPC_TIME_OUT*time.Second)
	if err != nil {
		return err
	}

	response := new(infra2.Response)
	response.Data = resp
	if err := json.Unmarshal(rlt.Data, response); err != nil {
		return err
	}

	if response.Code != 200 {
		return fmt.Errorf(`request failure, code: %v, message: %s`, response.Code, response.Message)
	}

	isOnline = true

	return nil
}

func (r *NatRpcDriver) UnRegister(guid string) error {
	if !isOnline {
		return nil
	}

	uri := `v2.rpc.apis.agent.unregsiter`
	rlt, err := r.nc.Request(uri, []byte(guid), RPC_TIME_OUT*time.Second)
	if err != nil {
		return err
	}

	response := new(infra2.Response)
	if err := json.Unmarshal(rlt.Data, response); err != nil {
		return err
	}

	if response.Code != 200 {
		return fmt.Errorf(`request failure, code: %v, message: %s`, response.Code, response.Message)
	}

	return nil
}

func (r *NatRpcDriver) Invoke(uri string, data interface{}) error {
	bt, err := json.Marshal(data)
	if err != nil {
		return err
	}

	rlt, err := r.nc.Request(uri, bt, RPC_TIME_OUT*time.Second)
	if err != nil {
		return err
	}

	response := new(infra2.Response)
	if err := json.Unmarshal(rlt.Data, response); err != nil {
		return err
	}

	if response.Code != 200 {
		return fmt.Errorf(`request failure, code: %v, message: %s`, response.Code, response.Message)
	}

	return nil
}

func GenUrl(guid, funcLable string) string {
	return fmt.Sprint(guid, `:`, funcLable)
}

func (r *NatRpcDriver) Call(rpcUrl string, params []byte) ([]byte, error) {
	return r.nc.IRequest(rpcUrl, params, RPC_TRY_INTERVAL*time.Second)
}

func (r *NatRpcDriver) Push(topic string, data []byte) error {
	return r.nc.IPublish(topic, data)
}

func (r *NatRpcDriver) Close() error {
	r.nc.IClose()
	return nil
}
